from itertools import chain
from django.db.models.fields import DateTimeField, DateField, CharField, TextField
from datetime import datetime
from django.db.models import Model
import json
from django.db.models.query import QuerySet

'''
封装django自带的将模型转成字典的方法，新增处理DateTimeField和DateField 
'''


def model_to_dict(instance, fields=None, exclude=None, replace=None, default=None):
    """
    :params instance: 模型对象，不能是queryset数据集
    :params fields: 指定要展示的字段数据，('字段1','字段2')
    :params exclude: 指定排除掉的字段数据,('字段1','字段2')
    :params replace: 将字段名字修改成需要的名字，{'数据库字段名':'前端展示名'}
    :params default: 新增不存在的字段数据，{'字段':'数据'}
    """
    # 对传递进来的模型对象校验
    if not isinstance(instance, Model):
        raise Exception('model_to_dict接收的参数必须是模型对象')
    # 对替换数据库字段名字校验
    if replace and type(replace) == dict:
        for replace_field in replace.values():
            if hasattr(instance, replace_field):
                raise Exception(f'model_to_dict,要替换成{replace_field}字段已经存在了')
    # 对要新增的默认值进行校验
    if default and type(default) == dict:
        for default_key in default.keys():
            if hasattr(instance, default_key):
                raise Exception(f'model_to_dict,要新增默认值，但字段{default_key}已经存在了')

    opts = instance._meta
    data = {}
    for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
        # 源码下：这块代码会将时间字段剔除掉，我加上一层判断，让其不再剔除时间字段
        if not getattr(f, 'editable', False):
            if type(f) == DateField or type(f) == DateTimeField:
                pass
            else:
                continue
        # 如果fields参数传递了，要进行判断
        if fields is not None and f.name not in fields:
            continue
        # 如果exclude 传递了，要进行判断
        if exclude and f.name in exclude:
            continue

        key = f.name
        # 获取字段对应的数据
        if type(f) == DateTimeField:
            # 字段类型是，DateTimeFiled 使用自己的方式操作
            value = getattr(instance, key)
            value = datetime.strftime(value, '%Y-%m-%d %H:%M:%S')
        elif type(f) == DateField:
            # 字段类型是，DateFiled 使用自己的方式操作
            value = getattr(instance, key)
            value = datetime.strftime(value, '%Y-%m-%d')
        elif type(f) == CharField or type(f) == TextField:
            # 字符串数据是否可以进行序列化，转成python结构数据
            value = getattr(instance, key)
            try:
                value = json.loads(value)
            except Exception as _:
                value = value
        else:#其他类型的字段
            # value = getattr(instance, key)
            key = f.name
            value = f.value_from_object(instance)
            # data[f.name] = f.value_from_object(instance)
        # 1、替换字段名字
        if replace and key in replace.keys():
            key = replace.get(key)
        data[key] = value
    #2、新增默认的字段数据
    if default:
        data.update(default)
    return data


def model_to_list(data: list, fields=None, exclude=None, replace=None, default=None):
    '''
    将queryset转成[{},{}] 格式
    :param data:
    :return:
    '''
    all_list = []
    if len(data) == 0:
        return all_list
    if not isinstance(data[0],Model):
        #第一个如果不是模型对象就报错
        raise Exception('model_to_list,内部元素必须是模型对象')

    for dic in data:
        try:
            all_list.append(model_to_dict(dic, fields=fields, exclude=exclude, replace=replace, default=default))
        except Exception as e:
            raise ValueError(str(e))
    return all_list


'''
使用
1、model_to_dict
user = models.User.objects.filter(id=10).first()
data_dic = model_to_dict(user)

2、model_to_list
users = models.User.objects.filter(is_delete=False)
data_lis = model_to_list(users)

'''